修复 Codex 图片生成 ACP 输出阻塞#295
Conversation
a6beb95 to
cd6389c
Compare
lornestack
left a comment
There was a problem hiding this comment.
Thanks for the fix — and for the thorough plan doc. We reproduced the bug and confirmed your root-cause analysis: codex-acp >= 0.14.0 (zed-industries/codex-acp#251, shipped in v0.14.0, which our migration 008 pins) maps Codex's ImageGenerationEnd to an ACP ToolCallUpdate whose raw_output.result embeds the whole image as base64 (a multi-MB single JSON-RPC message), and Codex often reports status: "generating" on that final event, so the tool call never reaches completed. Sanitizing at the ACP translation boundary is the right place, and the field contract (result_omitted / image.path) lines up with the AionUi frontend PR (iOfficeAI/AionUi#2935). Approach LGTM overall.
Two correctness issues need to be addressed before merge:
1. Oversized base64 still passes through when saved_path is missing
In sanitize_inline_image_result:
let should_omit = obj
.get("result")
.and_then(|v| v.as_str())
.map(|result| saved_path.is_some() && is_probably_inline_image_result(result))
.unwrap_or(false);saved_path.is_some() gates the stripping, so whenever Codex did not save the file (older codex versions, interrupted/failed generations), the multi-MB base64 still flows into WebSocket broadcast and SQLite — which is exactly the failure mode #297 describes. Suggestion: strip any oversized inline-image result unconditionally, and let saved_path only decide whether the image { path, mime_type, source } object is added.
Suggested regression test: a ToolCallUpdate whose raw_output has a >64KB iVBORw0KGgo... result but no saved_path — assert result is removed, result_omitted == true, no image object, and the status passes through unchanged.
2. failed status gets overridden to completed
normalize_tool_status returns Completed whenever the sanitized raw_output contains image.path, regardless of the SDK status — so a ToolCallStatus::Failed reported by the agent would be rewritten to completed. Suggestion: only normalize non-terminal statuses, e.g.:
match (image_saved, sdk_status.map(map_sdk_tool_status)) {
(true, None | Some(AcpToolCallStatus::Pending | AcpToolCallStatus::InProgress)) => {
Some(AcpToolCallStatus::Completed)
}
(_, status) => status,
}Suggested regression test: status: Failed + saved_path present — assert the event keeps failed and raw_output.status is not rewritten.
Non-blocking: docs/plans/2026-05-19-codex-image-acp-output.md is a 776-line implementation plan. We'd prefer to keep working plans out of the repo — please drop it from the PR (the ARCHITECTURE.zh-CN.md section is enough documentation).
Happy to merge once the two issues above are covered with tests.
|
Thanks @Jassy930 — the latest commit looks great. I went through it and confirmed all the requested changes are addressed:
One thing left before this can merge: the Test CI job fails to compile (not an assertion failure) because the branch is behind pub fn new(conversation_id, msg_id, turn_id, user_id, repo, broadcaster, cron_service)The new Could you merge/rebase the latest |
Strip oversized inline-image base64 from ToolCallUpdate raw_output unconditionally, regardless of whether saved_path is present. Older codex versions and interrupted/failed generations emit multi-MB base64 without saved_path; that payload must not reach WebSocket or SQLite (the failure mode in iOfficeAI#297). saved_path now only decides whether the structured image{path,mime_type,source} object is attached. Restrict status normalization to non-terminal statuses so a genuine Failed reported by the agent is preserved instead of being rewritten to completed. Add regression tests: - oversized base64 without saved_path is stripped, no image object, status passes through unchanged - Failed + saved_path keeps failed and does not rewrite raw_output.status Drop the 776-line working plan doc from the repo per review.
a603d37 to
38f94cd
Compare
|
配套关系标记:这个后端 PR 负责保留/持久化 Codex ACP image tool output 里前端可展示的 image patch 数据;聊天框真正展示图片还需要前端 PR iOfficeAI/AionUi#2935 一起合入。单独合入后端时,数据侧会具备图片输出,但当前 UI 不会完成图片预览渲染。 |
lornestack
left a comment
There was a problem hiding this comment.
Approving — all requested changes are in and CI is green.
Re-verified at head 38f94cd:
- Oversized inline-image
resultis stripped unconditionally;saved_pathonly gates the structuredimage { path, mime_type, source }object. normalize_tool_statuspreserves a terminalfailedand only promotes non-terminal statuses tocompleted.- The
run_acp_image_tool_call_update_persists_finish_without_base64test now matches the current 7-argStreamRelay::newsignature. - Full Test suite + Clippy + Format all pass.
Sanitizing at the ACP translation boundary is the right call, and the result_omitted / image.path contract lines up with the AionUi frontend PR (iOfficeAI/AionUi#2935). Thanks @Jassy930 for the careful follow-up — merging can proceed once the frontend PR is ready to go in together.
|
已补一个 PR,用来收紧 ACP image path/status 判断:#477 覆盖点:
|

变更说明
关联 Issue
Closes #297
验证